package com.whatech.mall.common.jwt;

import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.crypto.SecretKey;
import java.util.Date;
import java.util.Map;

/**
 * JWT工具类。
 */
@Slf4j
@RequiredArgsConstructor
@Component
public class JwtOperator {

    private final JwtProperties jwtProperties;

    /**
     * 从token中获取claim。
     *
     * @param token token
     * @return claim
     */
    public Claims getClaimsFromToken(String token) {
        try {
            return Jwts.parser()
                    .setSigningKey(this.jwtProperties.getSecret().getBytes())
                    .parseClaimsJws(token)
                    .getBody();
        } catch (ExpiredJwtException
                | UnsupportedJwtException
                | MalformedJwtException
                | IllegalArgumentException e) {
            log.error("json web token parse error", e);
            throw new JwtException("Token invalided.");
        }
    }

    /**
     * 获取token的过期时间。
     *
     * @param token token
     * @return 过期时间
     */
    public Date getExpirationDateFromToken(String token) {
        return getClaimsFromToken(token).getExpiration();
    }

    /**
     * 判断token是否过期。
     *
     * @param token token
     * @return 已过期返回true，未过期返回false
     */
    private Boolean isTokenExpired(String token) {
        Date expiration = getExpirationDateFromToken(token);
        return expiration.before(new Date());
    }

    /**
     * 计算token的过期时间。
     *
     * @return 过期时间
     */
    private Date getExpirationTime() {
        return new Date(System.currentTimeMillis() + this.jwtProperties.getExpirationInSecond() * 1000);
    }

    /**
     * 使用指定Payload生成token。
     *
     * @param claims 扩展信息
     * @return token
     */
    public String generateToken(Map<String, Object> claims) {
        Date createdTime = new Date();
        Date expirationTime = this.getExpirationTime();

        byte[] keyBytes = this.jwtProperties.getSecret().getBytes();
        SecretKey key = Keys.hmacShaKeyFor(keyBytes);

        return Jwts.builder()
                .setClaims(claims)
                .setIssuer(this.jwtProperties.getIss())
                .setSubject(this.jwtProperties.getSub())
                .setAudience(this.jwtProperties.getAud())
                .setIssuedAt(createdTime)
                .setExpiration(expirationTime)
                // 支持的算法详见：https://github.com/jwtk/jjwt#features
                .signWith(key, jwtProperties.getAlgorithm())
                .compact();
    }

    /**
     * 判断token是否非法。
     *
     * @param token token
     * @return 未过期返回true，否则返回false
     */
    public Boolean validateToken(String token) {
        return !isTokenExpired(token);
    }

}
